<script setup>
import {h, nextTick, onBeforeUnmount, reactive, ref, watch} from "vue";
import {useRoute, useRouter} from "vue-router";
import {ElNotification} from "element-plus";
import {staticData, user} from "@/store/index.js";
import {storeToRefs} from "pinia";

import {MdCatalog, MdPreview} from "md-editor-v3";
import "md-editor-v3/lib/style.css";
import {addLike, cancelLike, getIsLikeByIdOrIpAndType} from "@/api/like";
import Tooltip from "@/components/ToolTip/tooltip.vue";
import PageHeader from "@/components/PageHeader/index.vue";
import GsapCount from "@/components/GsapCount/index";

let setUpTimes = null;
let lastArticleId = null;
let comment = null,
  observe = null; // 用于监听评论是否出现在可视区域内
const commentRef = ref(null);
const commentIsOpen = ref(false);

const router = useRouter();
const route = useRoute();
// 初始化pinia
const staticStore = staticData();
const userStore = user();
const { codeTheme, previewTheme, mainTheme } = storeToRefs(staticStore);
const { getUserInfo } = storeToRefs(userStore);

const currentUrl = window.location.href;
const isLike = ref(false);
const likePending = ref(false);

// 模仿获取md文档信息
const mdState = reactive({
  text: "",
  id: "my-editor",
  switch: true,
});
const loading = ref(false);
const articleInfo = ref({});
const scrollElement = document.documentElement;
// 移动端目录是否可见
const drawerShow = ref(false);
// 推荐文章
const recommendList = ref([]);
const previousArticle = ref({});
const nextArticle = ref({});

const toggleDrawer = () => {
  drawerShow.value = !drawerShow.value;
};

const goToArticle = (article) => {
  router.push({ path: "/article", query: { id: article.id } });
};

// 文章点赞
const like = async () => {
  if (likePending.value) return;
  likePending.value = true;
  // 取消点赞
  if (isLike.value) {
    let res = await cancelLike({
      for_id: articleInfo.value.id,
      type: 1,
      user_id: getUserInfo.value.id,
    });
    if (res.code === 0) {
      articleInfo.value.thumbs_up_times--;
      isLike.value = false;
      likePending.value = false;

      ElNotification({
        offset: 60,
        title: "提示",
        message: h(
          "div",
          { style: "color: #7ec050; font-weight: 600;" },
          "有什么不足可以给我留下评论，感谢指正"
        ),
      });
    }
  }
  // 点赞
  else {
    let res = await addLike({
      for_id: articleInfo.value.id,
      type: 1,
      user_id: getUserInfo.value.id,
    });
    if (res.code === 0) {
      articleInfo.value.thumbs_up_times++;
      isLike.value = true;
      likePending.value = false;
      ElNotification({
        offset: 60,
        title: "提示",
        message: h("div", { style: "color: #7ec050; font-weight: 600;" }, "点赞成功，谢谢支持"),
      });
    }
  }
};
// 文章详情
const getArticleDetails = async (id) => {
  let res = {
    code: 0,
    message: "获取推荐文章成功",
    result: {
      article_content: "# 本地部署\n\n## 前端\n\n```node\n拉代码下来，cd到想启动项目的目录下(控制台 cd 文件夹名称)，比如blog-v3(cd blog-v3)，\n要在存在package.json文件的目录才能进行依赖下载、项目启动操作\n检查一下自己的node版本是否为18级以上(node -v) 博客后台blog-v3-admin必须18版本左右的才能成功运行和打包 版本超过20会失败，博客前台\nblog-v3使用vite4开发，需要node版本18以上才能运行，检查\n是否全局安装了pnpm(pnpm -v)，如果没有安装，需要自己npm i pnpm -g全局安装\n\n博客前台blog-v3直接pnpm i、pnpm run serve\n\n博客后台blog-v3-admin直接pnpm i、pnpm run serve\n```\n\n## 后端\n\n```node\n博客后端blog-server稍微复杂一点\n首先需要在本地的数据库内导入博客后端服务blog-server项目根目录下db/online_blog.sql文件\n还要在.env文件下配置数据库账户密码、上传模式(本地上传、七牛云或minio上传方式等)\n、如果是七牛云上传还要配置七牛云相关的，本地上传的话会直接保存在\n项目根目录下的upload/local文件夹下，通过项目地址可以直接访问图片(刚拉下\n来应该是没有local文件夹的，可以自己建一个)\n本地通过http://127.0.0.1:8888/upload/local/ + 图片名称进行访问。\n\n按照.env下的提示配置以后，就可以npm i、npm rum serve了\n后面会有.env内的具体配置教程\n\n注意: 超级管理员账户为admin，密码在env文件里配置，\n后台登录超级管理员以后，使用超级管理员给自己的账户管理员权限\n，就可以发布你的内容了，别使用超级管理员发布内容哦，\n用户表里没有超级管理员的信息，是我写在配置和代码里的\n```\n\n# 线上部署\n\n!!! successTIPS\n第一次部署推荐使用博主的方式 因为博主这样部署解决了很多坑 遇到的问题会少很多 也不会卡很久 自己熟悉了部署方面的知识可以自行发掘更多简单的部署方式\n主要是博客项目的文件位置和 nginx 里要对应上才能访问\n!!!\n\n推荐使用宝塔面板部署，上手简单一些\n\n## 服务器购买\n\n服务器大家可根据自己的需求购买 不过尽量买 linux/windows 的 其他系统可能部署不上去 博客对服务器性能不高 有个 2G 一核就可以部署了\n博主买过 阿里云轻量服务器、ESC 服务器 目前是使用的 ECS 服务器 99 一年 还行\n\n新手的话推荐体验一下各大厂商免费的云服务器。\n\n### 服务器连接\n\n在服务器厂商的服务器实例页面 可以看到远程连接的超链接 打开以后使用自己的密钥链接 或者是账户密码连接\n我的服务器在购买成功后就让我下载了一个 pom 文件 就是私钥 通过 在登录的时候选择 SSH 登录 并且上传自己的私钥 就可以登录了\n\n### 在服务器内安装宝塔\n\n登录成功以后 直接运行下面的命令就会自动安装宝塔\n\n```shell\nyum install -y wget && wget -O install.sh https://download.bt.cn/install/install_6.0.sh && sh install.sh ed8484bec\n```\n\n安装好以后，安装面板会给你展示访问宝塔内网、外网面板地址(记好外网面板地址，需要用这个在浏览器打开)、以及登录用户名和密码，类似下图（最新安装的提示会很全，偷懒网上找了个类似的老图）\n\n![](http://img.mrzym.top/Fvo6Ml5xmLepLTPm9B7Ep4jS39cw)\n\n## 连接宝塔面板\n\n记好用户名和密码，然后在浏览器内打开宝塔安装时给的外网面板地址，输入用户名和密码登录，登录好以后他会提示你下载一些软件，看到有 node、nginx、pm2 之类的就可以下载。\n\n## 宝塔首页大致功能预览\n\n![](http://img.mrzym.top/FohiAiBIIkhaaJZTKO1XTvKaZroo)\n\n## 配置博客后端服务\n\n坑：后端服务需要去在根目录的 www/wwwroot 下创建文件夹 blog，然后在里面将后端代码拉/复制过去(建议复制 blogServer 过去)，复制完成后运行 npm i 下载依赖（官方源下载有问题的话 npm 源设置成淘宝源试一试）。宝塔找文件是去 www/wwwroot 里去找的，所以要这样建立文件(最好将需要的文件都放在这里面，好找)。同时还需要去安全里将后端的 8888 端口放行，这样后端服务才能被访问到。\n\n### 文件路径大概展示\n\n![](http://img.mrzym.top/FihLDMYAFJSPdQ1SvNLTAWhnSYkv)\n\n### 文件夹内容展示\n\n![](http://img.mrzym.top/FsCnU0id6fi11jj01QrW4-QAze8x)\n\n### 放行后端服务端口\n\n如果不放行，后端服务会被宝塔防火墙禁掉，服务启动了也访问不到，还可以看看其他需要放行的端口是否放行了。服务器也是同理。\n![](http://img.mrzym.top/Fm7XYaUty53DAdiSB42Lx9TgCFkr)\n\n### mysql 数据库配置\n\n没有 mysql 就去软件商店下载一个，安装好以后新建 online_blog 数据库，导入博客根目录 db 文件夹下的数据库文件就 ok 了(有一次导入是上传文件，上传完成后还会显示上传成功的 sql 文件，还需要在那个文件右侧点击一次导入才算是导入成功)，导入过后可以重启一下 mysql，有可能会出现导入了但是数据库没重启就没生效的情况。\n\n![](http://img.mrzym.top/Fo82adq0OT8r7mj5Dk2AhG2mhI23)\n\n### 后端基础配置\n\n找到根目录下的.env 文件，里面包含项目后端的基础配置\n\n```\n# node项目启动地址\nAPP_PORT = 8888\n# 数据库地址\nMYSQL_HOST = 127.0.0.1\n# 数据库端口号\nMYSQL_PORT = 3306\n# 数据库连接名\nMYSQL_USER = root\n# 数据库密码 一定要用root密码 宝塔面板里有\nMYSQL_PASSWORD = ''\n# 数据库名称\nMYSQL_DB = online_blog\n# 超级管理员密码 超级管理员账户默认是admin 密码在这里自定义，然后通过管理员给你自己角色来获得修改权限\nADMIN_PASSWORD = ''\n\n# 不推荐 推荐用minio minio简单些\n# 七牛云 AK\nACCESSKEY = ''\n# 七牛云 SK\nSECRETKEY = ''\n# 七牛云存储空间名称\nBUCKET = ''\n\n# minio config 需要使用minio的就配置一下\n# minio AK\nMINIO_ACCESSKEY = ''\n\n# minio SK\nMINIO_SECRETKEY = ''\n\n# minio bucket\nMINIO_BUCKET = ''\n\n# minio服务地址 直接写服务器地址 或者是代理服务器的网址 比如博主的就是 mrzym.top 服务器的地址就是19.18.117.18之类的 不能带http://之类的请求头 末尾也最好不带 / 否则可能就会连不上 端口号也不能带 下面会配置\nMINIO_PATH = ''\n\n# minio 端口号 默认是 9000\nMINIO_PORT = 9000\n\n# local本地 qiniu  七牛云 online 云服务器 minio minio服务器\nUPLOADTYPE = 'minio'\n\n# 服务器地址 用于拼接图片显示 可以使用七牛云测试域名 前面请带上http://或者https://根据实际情况带上\n# 本地就是 'http://127.0.0.1:8888/' 像使用了七牛云绑定了自己的二级域名 博主的图片域名 'http://img.mrzym.top/'\n# 具体如何设置二级域名 可以百度 七牛云文档也有教程 如果嫌麻烦 可以使用minio 只需要在自己的服务器上装一个就行 请看博客部署教程\nBASEURL = 'http://127.0.0.1:8888/'\n\n# JWT密钥\nJWT_SECRET = blog\n\n```\n\n文件上传推荐先使用 minio 上传 上传模式选择 minio\nminio 安装方式 首先下载 docker 在软件安装面板搜来装就行 如果装不了 就在安装面板选择使用命令行的方式安装\n\n在宝塔里打开终端 执行下载 minio 的操作\n然后使用 docker 运行 minio\n\n```bash\n// 1、安装minio\ndocker pull minio/minio\n// 2、运行 minio\n// docker 运行minio 修改下面的user 和 password 那个就是minio登录账户密码 可以改成自己记得住的\ndocker run --name minio \\\n-p 9000:9000 \\\n-p 9999:9999 \\\n-d --restart=always \\\n-e \"MINIO_ROOT_USER=minio\" \\\n-e \"MINIO_ROOT_PASSWORD=minio@123\" \\\n-v /home/minio/data:/data \\\n-v /home/minio/config:/root/.minio \\\nminio/minio server /data \\\n--console-address '0.0.0.0:9999'\n```\n\n运行好以后 查看 docker 面板 会看到运行的 minio 然后打开服务器和宝塔的 9999、9000 端口进行放行 使用服务器 ip/域名 + :9999 登录\n![](/blog-images/qVPXdmhDXdMQ)\n登录\n![](/blog-images/ZoVvuRoMAGMP)\n登录后创建桶 桶的名称务必和我的一致 方便代理 后续熟悉了项目自己可以再修改\n![](/blog-images/GpoBaNhKRrSx)\n把桶的权限改为 public\n![](/blog-images/wCePuROQCRvf)\n创建 keys 然后把 ak、sk、桶的名称填入 env 配置里 就可以上传了\n![](/blog-images/sCSstfPjyaBe)\n\nonline 云服务器上传，云服务器上传是上传到自己的服务器的，也就是上传到 blogServer 项目 src 文件的 upload/online 下，只需在 env 里上传模式配置 online 模式即可(需要使用宝塔在此文件夹位置把权限改成读写权限，如下图)，优点就是很方便，缺点就是速度受服务器带宽影响，带宽低的服务器加载图片慢。\n![](/blog-images/VqIRTttAEtPH)\n\n\n如果要配置七牛云，上传模式请选择 qiniu\n七牛云文件上传 SDK：<https://developer.qiniu.com/kodo/1289/nodejs#5>\n**七牛云密钥获取 AK SK**\n\n![](http://img.mrzym.top/Fqcj4NrMIOHvAygC0w5rCVpg1mwI)\n\n**七牛云创建空间**\ntips：博主在对文档的时候选择的是华南地区测试的，没有测试其他地区的接口，所以都选择华南地区吧，不然会出现上传成功，但是结果为 undefined 的情况\n![](http://img.mrzym.top/FpeMZ6fliWXchtBY7ccrbpswyxXW)\n将密钥和空间名称填入.env 配置即可\n\n### 后端项目运行\n\n#### 使用 宝塔/网站/Node 运行项目\n1、找到位置\n![](/blog-images/UBPKCJXeXdqb)\n\n2、如果Node还没有下载的 先下载稳定版本的Node\n![](/blog-images/tEjdAdkuRSDe)\n\n3、添加项目\n![](/blog-images/bPfmRfIXxjej)\n\n!!! warning\n切记 打开设置后有一个开启外网映射的功能 开启了以后会有一个项目配置文件可以修改 如果没有特殊需求 请不要修改 因为我们后面会全局配置nginx 如果这里配置了 会和全局的有冲突\n!!!\n![](/blog-images/wyeBASGdgRWi)\n\n\n成功启动后可以看一下项目日志，如果日志如下图，则表明后端服务运行成功了，可以在浏览器输入服务器地址+ :端口号访问。若是出现数据库连接失败，就说明数据库账户、密码这些不对。\n![](/blog-images/BKjvpjOCgpry)\n\n2023/7/14 日 今天进行了部分敏感词的过滤，在遇到敏感词时后端需要调用接口来获取一句话代替敏感词，就引入了 request 来进行 http 请求，我的宝塔 node 环境里没有 request，需要在这个项目的终端里进行 npm i request --save(我为了防止运行不了，已经在依赖里加了，这里就是教大家如何找错)，自己可以先在宝塔文件夹下找到这个项目，使用终端打开这个项目，npm run serve 运行看看具体的报错，这样可以更清晰地解决问题。\n\n## 前端配置\n\n前端配置就是将前端项目打包的静态资源放到服务器上，通过 nginx 代理去访问，不涉及到创建服务去运行项目，前端项目此时是由浏览器作为环境运行的。\n在 www/wwwroot/blog 下新建 blogV3，然后分别建立 admin 存放博客前端后台运行 pnpm run build 打包的 dist 文件夹，blog 存放博客前端前台打包的 npm run build 打包的 dist 文件夹。[前端项目为什么需要打包](https://blog.csdn.net/YYece/article/details/106572951)\n\n前端项目存放如下图\n![](http://img.mrzym.top/Fv9f5gGn3UgM3RaBUanEVfVDevcX)\n\n## 宝塔 nginx 配置\n\n### 修改 nginx 配置\n\n在软件商店找到安装 nginx 好后(有小伙伴遇到了 nginx 下载后，启动了，但是 nginx 并没有生效的情况，可以在安装时选择编译安装就解决了)，点击配置，将我的配置复制进去就行\n![](http://img.mrzym.top/Foz4uuNVnGaHe1RGfqNCUpfPoY27)\n记得把服务器 ip 改成你的哦\n\n```nginx\nuser www www;\nworker_processes auto;\nerror_log /www/wwwlogs/nginx_error.log crit;\npid /www/server/nginx/logs/nginx.pid;\nworker_rlimit_nofile 51200;\n\nstream {\n  log_format tcp_format '$time_local|$remote_addr|$protocol|$status|$bytes_sent|$bytes_received|$session_time|$upstream_addr|$upstream_bytes_sent|$upstream_bytes_received|$upstream_connect_time';\n\n  access_log /www/wwwlogs/tcp-access.log tcp_format;\n  error_log /www/wwwlogs/tcp-error.log;\n  include /www/server/panel/vhost/nginx/tcp/*.conf;\n}\n\nevents {\n  use epoll;\n  worker_connections 51200;\n  multi_accept on;\n}\n\nhttp {\n  include mime.types;\n  #include luawaf.conf;\n\n  include proxy.conf;\n  lua_package_path \"/www/server/nginx/lib/lua/?.lua;;\";\n\n  default_type application/octet-stream;\n\n  server_names_hash_bucket_size 512;\n  client_header_buffer_size 32k;\n  large_client_header_buffers 4 32k;\n  client_max_body_size 50m;\n\n  sendfile on;\n  tcp_nopush on;\n\n  keepalive_timeout 60;\n\n  tcp_nodelay on;\n\n  fastcgi_connect_timeout 300;\n  fastcgi_send_timeout 300;\n  fastcgi_read_timeout 300;\n  fastcgi_buffer_size 64k;\n  fastcgi_buffers 4 64k;\n  fastcgi_busy_buffers_size 128k;\n  fastcgi_temp_file_write_size 256k;\n  fastcgi_intercept_errors on;\n\n  gzip on;\n  gzip_min_length 1k;\n  gzip_buffers 4 16k;\n  gzip_http_version 1.1;\n  gzip_comp_level 2;\n  gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml;\n  gzip_vary on;\n  gzip_proxied expired no-cache no-store private auth;\n  gzip_disable \"MSIE [1-6]\\.\";\n\n  limit_conn_zone $binary_remote_addr zone=perip:10m;\n  limit_conn_zone $server_name zone=perserver:10m;\n\n  server_tokens off;\n  access_log off;\n\n  # 数据库面板\n  server {\n    listen 888;\n    server_name phpmyadmin;\n    index index.html index.htm index.php;\n    root /www/server/phpmyadmin;\n    location ~ /tmp/ {\n      return 403;\n    }\n\n    #error_page   404   /404.html;\n    include enable-php.conf;\n\n    location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$ {\n      expires 30d;\n    }\n\n    location ~ .*\\.(js|css)?$ {\n      expires 12h;\n    }\n\n    location ~ /\\. {\n      deny all;\n    }\n\n    access_log /www/wwwlogs/access.log;\n  }\n\n  server {\n    listen 80;\n    server_name localhost;\n    # 博客前台前端静态资源\n    location / {\n      root /www/wwwroot/blog/blogV3/blog/dist;\n      index index.html index.htm;\n    }\n    # 后台前端静态资源\n    location /admin {\n      alias /www/wwwroot/blog/blogV3/admin/dist;\n      index index.html index.htm;\n    }\n    # 小表情\n    location /emoji {\n      alias /www/wwwroot/emoji;\n      autoindex on;\n    }\n\n    location /ws/ {\n        proxy_pass http://服务器ip:8889/;  \n        # 后端WebSocket服务器的地址和端口\n        proxy_http_version 1.1; \n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection \"Upgrade\";\n        proxy_set_header Host $host; \n        proxy_cache_bypass $http_upgrade; \n    }\n\n    # 服务器存储图片\n    location /online {\n      alias /www/wwwroot/blog/blogServer/src/upload/online;\n      autoindex on;\n    }\n    # 后端服务代理\n    location /api/ {\n      proxy_pass http://服务器ip:8888/; # 服务端代理地址 或者是域名(域名解析以后要能指向服务器ip)\n      proxy_set_header Host $host; # 获取用户真实ip\n      proxy_set_header X-Real-IP $remote_addr;\n      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n    }\n    # gitee 代理\n    location /gitee/ {\n      proxy_pass https://gitee.com/mrzym/; # 改成你的gitee面板地址\n    }\n    # minio 代理\n    location /blog-images {\n      proxy_pass http://服务器ip:9000/blog-images;\n    }\n    # 网易云音乐 代理\n    location /wapi/ {\n      proxy_pass http://服务器ip:3000/;\n    }\n  }\n  include /www/server/panel/vhost/nginx/*.conf;\n}\n```\n\n### 音乐代理\n\n[音乐项目文档地址](https://binaryify.github.io/NeteaseCloudMusicApi/#/)\n由于这个项目为保护版权 已经不能下载源码了 所以需要的可以进 qq 群 我在群里分享的有\n把这个项目拉到自己的服务器下，使用 pm2 运行起来(找到 app.js 运行)，然后将音乐 api 的代理加入 nginx 里就可以了\n\n## 写到最后\n\nps: 获得管理员权限，可以登录超级管理员去给自己的账户权限，也可以去数据库 user 表里把自己的角色改为管理员，也就是 role 的值改为 1。\n\n由于博客还在不断完善中，所以难免会有一些 bug 刚开发完没有测试到，希望大家在部署过程中遇到问题可以多问我，遇到 bug 多给我提一些，我可以在给你们解决问题的同时完善部署文档，同时还可以解决一些 bug。\n\n到这里就大功告成啦！！！\n",
      article_cover: "http://img.mrzym.top/FtAnnRvDr78EeHrSJc9IM3fabRiN",
      article_description: "这篇文章是在第一篇普通部署教程的基础上，使用宝塔面板部署的，确实方便了很多。",
      article_title: "博客部署教程-宝塔面板",
      authorName: "小张",
      author_id: 1,
      categoryName: "博客部署",
      category_id: 2,
      createdAt: "2023-05-19 19:47:44",
      id: 6,
      is_top: 1,
      order: 1,
      origin_url: null,
      reading_duration: 8007662953,
      status: 1,
      tagIdList: [6, 9, 7],
      tagNameList: ["linux", "阿里云轻量服务器", "宝塔面板"],
      thumbs_up_times: 94,
      type: 1,
      updatedAt: "2024-09-20 01:01:26",
      view_times: 10502
    }
  }
  if (res.code === 0) {
    mdState.text = res.result.article_content;
    articleInfo.value = res.result;

    const LRes = {
      code: 0,
      message: "获取用户是否点赞成功",
      result: false
    }
    if (LRes.code === 0) {
      isLike.value = LRes.result;
    }
  }
};

// 返回本次浏览时长
const addReadingDuration = async (id) => {
  const now = new Date();
  return 3600;
};

// 推荐文章
const getRecommendArticle = async (id) => {
  let res = {
    code: 0,
    message: "获取推荐文章成功",
    result: {
      next: {
        article_cover: "http://img.mrzym.top/FhB8dbo7d3CBgLEMXHwLZajU16oD",
        article_title: "mac快速切换node版本",
        id: 7
      },
      previous: {
        article_cover: "http://img.mrzym.top/Fn6hrb8r86xBIBwgjDxnVLLl6oP9",
        article_title: "vue3项目页面首次加载慢优化",
        id: 3
      },
      recommend: {
        article_cover: "http://img.mrzym.top/FtAnnRvDr78EeHrSJc9IM3fabRiN",
        article_title: "博客部署教程-宝塔面板",
        createdAt: "2023-05-19 19:47:44",
        id: 6
      }
    }
  }
  if (res.code === 0) {
    const { previous, next, recommend } = res.result;
    recommendList.value = recommend;
    previousArticle.value = previous;
    nextArticle.value = next;
  }
};

const init = async (id) => {
  loading.value = true;
  await getArticleDetails(id);
  await getRecommendArticle(id);
  loading.value = false;

  nextTick(() => {
    commentIsOpen.value = false;
    observeBox();
  });
};

// 无限加载
const observeBox = () => {
  // 获取要监听的元素
  comment = document.querySelector(".comment-box");

  observe = new IntersectionObserver(
    (entries) => {
      entries.forEach(async (e) => {
        if (e.isIntersecting && e.intersectionRatio > 0 && !commentIsOpen.value) {
          commentRef.value.toggleExpand();
          commentIsOpen.value = true;
        }
      });
    },
    { threshold: [1] }
  );
  comment && observe.observe(comment);
};
watch(
  () => route,
  (newV) => {
    // 初始化的时候 记录进来的时间
    setUpTimes = new Date();
    lastArticleId = newV.query.id;

    if (newV.path === "/article" && newV.query.id) {
      init(newV.query.id);
    }
  },
  {
    immediate: true,
    deep: true,
  }
);
// 用户离开页面时增加阅读时长
onBeforeUnmount(() => {
  if (setUpTimes) {
    addReadingDuration(lastArticleId); // 添加阅读时长
  }
});
</script>

<template>
  <PageHeader :article="articleInfo" :loading="loading" />
  <div class="article">
    <el-row class="article_box">
      <el-col :xs="24" :sm="18">
        <el-skeleton v-if="loading" :loading="loading" :rows="8" animated />
        <el-card v-else class="md-preview">
          <MdPreview
            class="md-preview-v3"
            v-model="mdState.text"
            :editorId="mdState.id"
            :preview-theme="previewTheme"
            :code-theme="codeTheme"
            :theme="mainTheme ? 'dark' : 'light'"
          ></MdPreview>
          <div class="article-info">
            <div class="article-info-inner">
              <div>
                <span>文章作者：</span>
                <a class="to_pointer" href="https://gitee.com/mrzym">{{
                  articleInfo.authorName
                }}</a>
              </div>
              <div>
                <span>类型：</span>
                <el-tag>{{
                  articleInfo.type == 1 ? "原创" : articleInfo.type == 2 ? "转载" : "翻译"
                }}</el-tag>
              </div>
              <div v-if="articleInfo.type != 1">
                <span>原文链接：</span>
                <a class="to_pointer" :href="articleInfo.origin_url">{{
                  articleInfo.origin_url
                }}</a>
              </div>
              <div v-else>
                <span>本文链接：</span>
                <a class="to_pointer" v-copy="currentUrl">{{ currentUrl }}</a>
              </div>
              <p>声明: 此文章版权归 Mr M 所有，如有转载，请注明来自原作者</p>
            </div>
          </div>
          <div :class="['like', isLike ? 'is-like' : '']" @click="like">
            <i class="iconfont icon-icon1 !mr-[5px]"></i>
            <GsapCount
              :class="[isLike ? 'is-like' : '']"
              v-if="articleInfo.thumbs_up_times - 0 < 1000"
              :value="articleInfo.thumbs_up_times"
            />
            <span v-else :class="[isLike ? 'is-like' : '']">
              {{ articleInfo.thumbs_up_times }}
            </span>
          </div>
          <div class="recommend flex_r_between">
            <div class="recommend-box" @click="goToArticle(previousArticle)">
              <el-image
                class="recommend-box-img animate__animated animate__fadeInDown"
                fit="cover"
                :src="previousArticle.article_cover"
              >
                <template #error>
                  <svg-icon name="image404" :width="10" :height="5"></svg-icon>
                </template>
              </el-image>
              <span class="recommend-box-item prev">
                <span class="flex_r_around">
                  <i class="iconfont icon-arrowleft"></i>
                  <span class="font-semibold">上一篇</span>
                </span>
                <Tooltip
                  width="60%"
                  color="#fff"
                  :weight="600"
                  :name="previousArticle.article_title"
                  align="left"
                ></Tooltip>
              </span>
            </div>
            <div class="recommend-box" @click="goToArticle(nextArticle)">
              <el-image
                class="recommend-box-img animate__animated animate__fadeInDown"
                fit="cover"
                :src="nextArticle.article_cover"
              >
                <template #error>
                  <svg-icon name="image404" :width="10" :height="5"></svg-icon>
                </template>
              </el-image>
              <span class="recommend-box-item next">
                <span class="flex_r_around">
                  <span class="font-semibold">下一篇</span>
                  <i class="iconfont icon-arrowright"></i>
                </span>
                <Tooltip
                  width="60%"
                  color="#fff"
                  :weight="600"
                  :name="nextArticle.article_title"
                  align="right"
                ></Tooltip>
              </span>
            </div>
          </div>
          <!-- 移动端推荐文章 -->
          <div class="mobile-recommend">
            <el-row style="padding: 2rem">
              <div class="recommend-title">推荐文章</div>
              <el-col
                :span="12"
                v-for="item in recommendList"
                :key="item.id"
                @click="goToArticle(item)"
              >
                <el-card class="card card-hover">
                  <template #header>
                    <span :title="item.article_title" class="title">{{ item.article_title }}</span>
                  </template>
                  <el-image
                    class="image animate__animated animate__fadeInDown"
                    fit="cover"
                    :src="item.article_cover"
                  >
                    <template #error>
                      <svg-icon name="image404" :width="10" :height="5"></svg-icon>
                    </template>
                  </el-image>
                </el-card>
              </el-col>
            </el-row>
          </div>
          <div class="!p-[2rem] comment-box">
            <Comment
              ref="commentRef"
              class="w-[100%]"
              type="article"
              :id="route.query.id - 0"
              :author-id="articleInfo.author_id"
            />
          </div>
        </el-card>
      </el-col>
      <el-col :xs="0" :sm="6">
        <el-skeleton v-if="loading" :loading="loading" :rows="3" animated />
        <el-card v-else class="command card-hover" header="推荐文章">
          <div class="command-box">
            <div
              class="command-box-item"
              v-for="(item, index) in recommendList"
              :key="index"
              @click="goToArticle(item)"
            >
              <el-image
                class="command-box-item__img animate__animated animate__fadeInDown"
                fit="cover"
                width="50"
                :src="item.article_cover"
              >
                <template #error>
                  <svg-icon name="image404" :width="5" :height="5"></svg-icon>
                </template>
              </el-image>
              <Tooltip width="35%" weight="600" size="1rem" :name="item.article_title" />
              <Tooltip width="35%" size="0.8rem" :name="item.createdAt" />
            </div>
          </div>
        </el-card>
        <el-affix :offset="53" style="width: inherit">
          <el-skeleton v-if="loading" :loading="loading" :rows="5" animated />
          <el-card v-else class="catalogue-card card-hover" header="目录">
            <div class="catalogue-card__box">
              <MdCatalog :editorId="mdState.id" :scroll-element="scrollElement" />
            </div>
          </el-card>
        </el-affix>
      </el-col>
    </el-row>
    <div class="mobile-affix">
      <i class="iconfont icon-arrowright" @click="toggleDrawer"></i>
    </div>
    <!-- 移动端目录 -->
    <el-drawer
      title="目录"
      v-model="drawerShow"
      direction="ltr"
      :before-close="toggleDrawer"
      :append-to-body="true"
      size="60%"
      :z-index="9999"
    >
      <MdCatalog v-if="!loading" :editorId="mdState.id" :scroll-element="scrollElement" />
    </el-drawer>
  </div>
</template>

<style lang="scss" scoped>
.article {
  &-info {
    padding: 2rem 2rem;

    &-inner {
      padding: 1rem;
      color: var(--font-color);
      border: 1px solid rgba(255, 255, 255, 0.3);
    }
  }
}

.catalogue-card {
  margin-top: 1rem;
  padding: 1rem 0.5rem;

  &__box {
    scrollbar-width: none;
    overflow: auto;
    max-height: calc(100vh - 23.1rem);
    cursor: pointer;
  }
}

.mobile-catalog {
  padding: 2rem;
  max-height: 400px;
  scrollbar-width: none;
  overflow-y: auto;
  cursor: pointer;
}

.theme-card {
  padding: 1rem 0.5rem;
}

.command {
  padding: 1rem 0.5rem;

  &-box {
    max-height: 160px;
    scrollbar-width: none;
    overflow-y: auto;
    cursor: pointer;

    &::-webkit-scrollbar {
      display: none;
      /* Chrome Safari */
    }

    &-item {
      display: flex;
      justify-content: flex-start;
      align-items: center;
      padding: 0.3rem;
      color: var(--font-color);

      &__img {
        margin-right: 1rem;
        width: 50px;
        height: 40px;
      }
    }
  }
}

.icon-sort {
  font-size: 1.8rem;
  color: var(--font-color);
}

.recommend {
  box-sizing: border-box;
  position: relative;
  padding: 2rem;

  &-box {
    display: flex;
    justify-content: space-between;
    align-items: center;
    position: relative;
    width: 50%;
    height: 100%;
    overflow: hidden;
    color: var(--global-white);
    transition: scale 0.5s;
    cursor: pointer;

    &:hover {
      .recommend-box-img {
        scale: 1.2;
      }
      .recommend-box-item {
        background-color: var(--mask-bg);
      }
    }

    &-img {
      transition: all 0.5s;
      width: 100%;
      height: 100%;
    }

    &-item {
      position: absolute;
      display: flex;
      flex-direction: column;
      justify-content: center;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      font-size: 1.2rem;
      line-height: 1.8;
      transition: all 0.5s;
      background-color: rgba(0, 0, 0, 0.7);

      i {
        font-size: 1.4rem;
      }
    }

    .prev {
      padding-left: 2rem;
      align-items: flex-start;

      div {
        box-sizing: border-box;
        max-width: 10rem;
        font-size: 1rem;
        margin-left: 1rem;
      }
    }

    .next {
      padding-right: 2rem;
      align-items: flex-end;

      div {
        box-sizing: border-box;
        max-width: 10rem;
        font-size: 1rem;
        margin-right: 1rem;
      }
    }
  }
}

.like {
  margin: 1rem;
  display: flex;
  justify-content: center;
  align-items: center;

  .icon-icon1 {
    font-size: 1.8rem;
    transition: all 0.3s;
    &:hover {
      scale: 1.1;
    }
  }
}

.is-like {
  font-size: 1.2rem;
  font-weight: 600;
  color: var(--primary);
  .icon-icon1 {
    font-size: 1.8rem;
    color: var(--primary);
  }
}

.mobile-recommend {
  position: relative;
  .recommend-title {
    position: absolute;
    top: 0;
    left: 2.2rem;
    font-size: 1.2rem;
    font-weight: 600;
    color: var(--font-color);
  }
  .card {
    width: 100%;
    height: 8rem;
    overflow: hidden;
  }
  .title {
    display: inline-block;
    width: 80%;
    height: 2rem;
    padding: 0.3rem 0 0 0.3rem;
    font-size: 1rem;
    text-overflow: ellipsis;
    white-space: nowrap;
    overflow: hidden;
  }
  .image {
    width: 100%;
    height: 6rem;
  }
}

:deep(.el-card__header) {
  font-size: 1.6rem;
  padding: 0 !important;
  font-weight: bold;
  line-height: 1.8;
  color: var(--font-color);
}

a {
  text-decoration: underline;
}
@media screen and (min-width: 768px) {
  .mobile-recommend {
    display: none;
  }
}
</style>
